home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 090 / pctj8409.arc / X2OKI.ASM < prev   
Assembly Source File  |  1986-09-14  |  12KB  |  335 lines

  1.  
  2. ;**********************************************************************************
  3. ;* This program traps the printer output interrupt (17H), and translates outgoing *
  4. ;* IBM-style line drawing characters to codes compatible with the Okidata 84p.    *
  5. ;*                                                                                *                   
  6. ;* Since the 84p doesn't fully implement all double-line characters, we translate *
  7. ;* everything into single-line graphics.  Other graphics characters (hearts,      *
  8. ;* happy faces, etc.) are not supported in this program.                          *
  9. ;*                                                                                *
  10. ;* The utility should be disabled prior to running any program which depends      *
  11. ;* on the Okidata graphics character set or uses the Okigraph graphics printing   *
  12. ;* facility (such as Lotus 1-2-3).                                                * 
  13. ;*                                                                                *  
  14. ;* USE:  Giving the command "X2OKI" for the first time will install the           *
  15. ;* conversion utility, and report "X2Oki installed and running."                  *
  16. ;* Thereafter, giving the "X2Oki" command will toggle the translation             *
  17. ;* feature off and on, reporting "X2Oki temporarily deactivated" or               *
  18. ;* "X2Oki reactivated."                                                           *
  19. ;*                                                                                * 
  20. ;*                                                                                * 
  21. ;* Copyright (C) 1983 Jeffrey P. Garbers.   All rights reserved.                  *
  22. ;********************************************************************************** 
  23.  
  24.  
  25.  
  26. CSEG    segment para public 'CODE'
  27.     assume    cs:CSEG, ds:CSEG
  28.  
  29.  
  30. ; The printer interrupt number is defined by the PC to be 17H.
  31.  
  32. PRINTER_INTERRUPT    equ    17H
  33.  
  34. ; The signature word is an arbitrary 16-bit number that we'll use in
  35. ;  checking to see if the utility has already been installed.  
  36.  
  37. SIGNATURE    equ    0944H
  38.  
  39. ; DOS is a handy macro that calls DOS for service.  Its first argument
  40. ;  is a function code number.  The second one (if it's there) is assumed
  41. ;  to be an offset which needs to be loaded into DX.
  42.  
  43. DOS    macro    fcn_code, location_arg
  44.     mov    ah, fcn_code
  45.     ifnb    <location_arg>
  46.     mov    dx, offset location_arg
  47.     endif
  48.     int    21H
  49.     endm
  50.  
  51. ; A couple of DOS function codes, defined.
  52.  
  53. PRINT_MESSAGE    equ    09H
  54. SET_VECTOR    equ    25H
  55.  
  56.  
  57. ; Programs to be passed through the EXE2BIN utility need to start at
  58. ;  address 100H.
  59.  
  60.     org    100H            ; goes thru EXE2BIN
  61.  
  62. ; Since PC-DOS will always start us at 100H, we need to do a jump right
  63. ;  away to get around the data and interrupt-handling stuff, and get
  64. ;  right to the program initialization.
  65.  
  66. HOME:    jmp    START            ; skip around all the data space
  67.  
  68.  
  69. ; Data areas for X2OKI.
  70.  
  71. ; BIOS_HANDLER contains the doubleword address which used to be the 
  72. ;  printer interrupt handler.  We leave here through the old vector, so any
  73. ;  other interceptors that may have been installed (spoolers, etc.) will
  74. ;  still work properly.
  75.  
  76. BIOS_HANDLER    dd    ?        ; address of former int handler
  77.  
  78. ; The translation tables.  The first table (PC_CHARS) contains the
  79. ;  IBM-defined codes for the single and double line box drawing characters.
  80. ;  The parallel second table (OKI_CHARS) contains the Okidata 84p codes
  81. ;  for those characters.  Notice that the lines of the OKI_CHARS table
  82. ;  are quite similar; this is because PC double-line characters are translated
  83. ;  to Oki single-liners as mentioned in the introduction.  NUMBER_OF_CHARS
  84. ;  just lets the assembler figure out how many of these we have to look thru.
  85.  
  86. ; In order to expand the conversion tables, just add codes to both tables
  87. ;  as needed, making sure that you keep the order straight.
  88.  
  89. PC_CHARS    equ    this byte
  90.     db    218,194,191,195,180,192,193,217,196,179,197    ; sng boxes
  91.     db    201,203,187,204,185,200,202,188,205,186,206    ; dbl boxes
  92.     db    214,210,183,199,182,211,208,189,215        ; combinations
  93.     db    213,209,184,198,181,212,207,190,216        ; more combos
  94.  
  95. NUMBER_OF_CHARS    equ    $-PC_CHARS
  96.  
  97. OKI_CHARS    equ    this byte
  98.     db    152,145,153,147,146,154,144,155,149,150,143
  99.     db    152,145,153,147,146,154,144,155,149,150,143
  100.     db    152,145,153,147,146,154,144,155,143
  101.     db    152,145,153,147,146,154,144,155,143
  102.  
  103. ; An area to save the incoming character, which we need to preserve.
  104.  
  105. INCOMING_CHAR    db    ?
  106.  
  107. ;************************************************************************
  108. ;        START OF X2Oki CODE
  109. ;************************************************************************
  110.  
  111.  
  112. MAIN    proc    far
  113.  
  114. ; The lodged part.  This translates Oki chars to our chars.
  115.     
  116. HANDLER:    jmp    short HANDLE1            ; skip data
  117.  
  118. ; Don't change that jump, or put anything between here and the INSTALLED
  119. ;  and ACTIVE variables.  Later we assume that they come right after
  120. ;  the start of the handler.
  121.  
  122. ; The INSTALLED variable is just an instance of the signature word.  We
  123. ;  can check for the presence of this word to see if the driver has
  124. ;  already been installed.
  125.     
  126. INSTALLED    dw    SIGNATURE
  127.  
  128. ; ACTIVE is 1 if the utility is active.  If it's zero, we just pass
  129. ;  characters right through and don't do any conversion.
  130.  
  131. ACTIVE        db    1                ; we are active
  132.  
  133.  
  134.  
  135. ; Watch for the use of the CS: override prefixes in here.  When we get
  136. ;  control, the only segment register we know about is CS (the Code
  137. ;  Segment), so we'll use it to check to see if we're running.
  138.  
  139.  
  140. HANDLE1:    
  141.  
  142.     mov    cs:INCOMING_CHAR, al        ; save incoming character
  143.  
  144.  
  145. ; There are three cases in which we want to do nothing here and just
  146. ;  skip right out to the HANDLEdone location (which passes control
  147. ;  off to the old BIOS handler).  Those are:
  148. ;
  149. ;    (1) Utility not currently active.
  150.  
  151.     cmp    cs:ACTIVE, 1            ; are
  152.     jnz    HANDLEdone            ; we're not running
  153.  
  154. ;    (2) Request to BIOS is not a "please print character" call,
  155. ;        but rather a status or other request.
  156.  
  157.     or    ah, ah
  158.     jnz    HANDLEdone            ; not a print call
  159.  
  160. ;    (3) The character to be printed has a value below 128, and
  161. ;        therefore can't be a line drawing character.  Testing
  162. ;        for this allows us to pass most of the printing characters
  163. ;        through quickly without having to scan the table.
  164.  
  165.     test    al, 80H
  166.     jz    HANDLEdone            ; not a graphics char
  167.  
  168. ; Okay, we have a character we may wish to do something to.  First, let's
  169. ;  save a few registers.
  170.  
  171.     pushf                    ; need to save this because
  172.     push    es                ; we're going to mess with
  173.     push    cx                ; the direction flag during
  174.     push    di                ; our scanning.
  175.  
  176.  
  177. ; The following two-instruction sequence gets a copy of CS into the ES
  178. ;  register.  We will be scanning the table which exists in this segment.
  179.  
  180.     push    cs
  181.     pop    es
  182.  
  183. ;  When scanning a table, you must:
  184.  
  185. ;    (1) Point ES:DI to the start of the table (we've already done ES)
  186.  
  187.     mov    di, offset PC_CHARS
  188.  
  189. ;    (2) Set CX to the size of the table
  190.  
  191.     mov    cx, NUMBER_OF_CHARS
  192.  
  193. ;    (3) Set or clear the direction flag to indicate the direction
  194. ;        of the scan (normally CLD)
  195.  
  196.     cld
  197.  
  198. ; Now everything is set up for the scan.  Let's use the nice 8088/8086
  199. ;  instructions to do it.  The one we're going to use will scan bytes
  200. ;  until it finds a match, or until it runs out of table.  The REPNZ
  201. ;  prefix means "repeat this instruction while there isn't a match".
  202.  
  203.     repnz    scasb                ; look it up
  204.     jnz    HANDLEscanDone            ; couldn't find it
  205.  
  206.  
  207. ; Now DI points one past the matching entry (remember that DI is always
  208. ;  adjusted, even if we find a match.  We now need to convert DI
  209. ;  into an offset into the OKI_CHARS table.  Since we started DI at
  210. ;  the offset of PC_CHARS, and since we're one beyond our match, 
  211. ;  we can subtract one more than our starting spot to find our offset
  212. ;  into the OKI_CHARS table.  Messy?  A little.  Work it out on paper if
  213. ;  you have trouble picking up on this notion.
  214.  
  215.     sub    di, offset PC_CHARS+1        ; make it an offset
  216.     mov    al, es:OKI_CHARS[di]
  217.  
  218. ; Okay, either we've translated the character or we don't know how to translate
  219. ;  it.  Recover the registers we saved, and split out to the original
  220. ;  BIOS handler.
  221.  
  222. HANDLEscanDone:
  223.  
  224.     pop    di
  225.     pop    cx
  226.     pop    es
  227.     popf
  228.  
  229. ; Notice again the use of the CS override here.  We have not used the
  230. ;  DS register at all during this handler.
  231.  
  232. HANDLEdone:    pushf            ; must PUSH before manually calling
  233.     call    cs:BIOS_HANDLER        ; an interrupt handler
  234.     mov    al, cs:INCOMING_CHAR    ; recover original character codes
  235.     iret                ; and return to caller
  236.  
  237. ; Since we're going to be doing an end-but-stay-resident, we'll need to
  238. ;  mark where the handler part ends.  The label LAST will serve nicely.
  239.  
  240. LAST    equ    this byte            ; last part of resident
  241.  
  242.  
  243.  
  244. ;************************************************************************
  245. ;         TRANSIENT PART of X2Oki
  246. ;
  247. ;    See text of article about the difference between the resident
  248. ;    and transient parts of an interceptor program.
  249. ;
  250. ;************************************************************************
  251.  
  252. START:    push    ds
  253.     xor    ax, ax
  254.     push    ax                ; .COM programs start like this
  255.  
  256. ; AX is already zero, and we want to look at an interrupt handler's address
  257. ;  (which lives in segment 0000).  So let's just move our zeroed AX into
  258. ;  ES for snooping purposes.  We'll set SI to 17H (the printer service
  259. ;  interrupt) times 4 (the number of bytes in each handler address).
  260.  
  261.     mov    es, ax
  262.     mov    si, PRINTER_INTERRUPT*4            ; point to installed address
  263.  
  264. ; First, we'll get the address that's there so we can call it later.
  265.  
  266.     lods    word ptr es:[si]
  267.     mov    word ptr BIOS_HANDLER, ax
  268.     lods    word ptr es:[si]
  269.     mov    word ptr BIOS_HANDLER+2, ax
  270.  
  271. ; We've now got the old handler in BIOS_HANDLER.  Pick it up as a 32-bit
  272. ;  pointer so we can see who's there.
  273.     
  274.     les    si, BIOS_HANDLER
  275.  
  276. ; We know that if it's our handler, there's the SIGNATURE word two bytes
  277. ;  beyond the beginning of the handler (our handler starts with a short
  278. ;  jump).  See if it's us, and if it isn't, install us.
  279.  
  280.     cmp    word ptr es:[si+2], SIGNATURE    ; this us?
  281.     jnz    INSTALL                ; not yet-- install us
  282.  
  283. ; Toggle the on/off setting.  The ACTIVE flag lives four bytes beyond the
  284. ;  handler-- let's flop its setting.
  285.  
  286.     mov    al, es:[si+4]            ; pick up running flag
  287.     xor    al, 1                ; flip the bit
  288.     mov    es:[si+4], al            ; replace it
  289.  
  290. ; Okay, we now have AL as the new state.  Give a message saying
  291. ;  our current state.
  292.  
  293.     mov    dx, offset m$RUNNING        ; for now, assume it's running
  294.     cmp    al, 1                ; were we right?
  295.     jz    MSG_N_SPLIT            ; yes, take this
  296.     mov    dx, offset m$OFFNOW        ; report "off"
  297.  
  298. ; When we get here, DX is pointing to a status message.   Print it and
  299. ;  leave.
  300.  
  301. MSG_N_SPLIT:    DOS    PRINT_MESSAGE        ; "print message" function
  302.     ret                    ; goodbye!
  303.  
  304. ; If we get to this spot, it's the first time that the program has been
  305. ;  run.  Install our own handler into the interrupt vector, report this
  306. ;  event with a message, and let DOS know we want to stay around.
  307.  
  308. INSTALL:
  309.  
  310.     mov    al, PRINTER_INTERRUPT    ; printer interrupt, please
  311.     DOS    SET_VECTOR HANDLER    ; DOS function 25h is "set interrupt"
  312.  
  313.     DOS    PRINT_MESSAGE m$NOWREADY
  314.  
  315. ; Everything's complete.  Leave the program via INT 27H with DX pointing to
  316. ;  the end of the resident part, and DOS'll keep the important stuff around
  317. ;  while freeing up the memory occupied by the part that's not part of
  318. ;  the interrupt handler per se.
  319.  
  320.     mov    dx, offset LAST+1    ; point to the end of the fixed part 
  321.     int    27H            ; end-but-stick-around
  322.  
  323.  
  324. ; Three status report messages.
  325.  
  326. m$NOWREADY    db    "X2Oki installed and running.$"
  327. m$RUNNING    db    "X2Oki reactivated.$"
  328. m$OFFNOW    db    "X2Oki temporarily deactivated.$"
  329.  
  330. MAIN     endp
  331.  
  332. CSEG    ends
  333.     end    HOME
  334. 
  335.